Expand description
A simple, powerful template engine with minimal dependencies and configurable delimiters.
Overview
Syntax
- Expressions:
{{ user.name }}
- Conditionals:
{% if user.enabled %} ... {% endif %}
- Loops:
{% for user in users %} ... {% endfor %}
- Nested templates:
{% include "nested" %}
- Configurable delimiters:
<? user.name ?>
,(( if user.enabled ))
- Arbitrary user defined filters:
{{ user.name | replace: "\t", " " }}
Engine
- Clear and well documented API
- Customizable value formatters:
{{ user.name | escape_html }}
- Render to a
String
or anystd::io::Write
implementor - Render using any
serde
serializable values - Convenient macro for quick rendering:
upon::value!{ name: "John", age: 42 }
- Pretty error messages when displayed using
{:#}
- Format agnostic (does not escape values for HTML by default)
- Minimal dependencies and decent runtime performance
Why another template engine?
It’s true there are already a lot of template engines for Rust!
I created upon
because I required a template engine that had runtime
compiled templates, configurable syntax delimiters and minimal dependencies.
I also didn’t need support for arbitrary expressions in the template syntax
but occasionally I needed something more flexible than outputting simple
values (hence filters). Performance was also a concern for me, template
engines like Handlebars and Tera have a lot of features but can be up to
five to seven times slower to render than engines like TinyTemplate.
Basically I wanted something like TinyTemplate with support for configurable delimiters and user defined filter functions. The syntax is inspired by template engines like Liquid and Jinja.
MSRV
Currently the minimum supported version for upon
is Rust 1.65. Disabling
the filters
feature reduces it to Rust 1.60. The MSRV will only ever
be increased in a breaking release.
Getting started
First, add the crate to your Cargo manifest.
cargo add upon
Now construct an Engine
. The engine stores the syntax config, filter
functions, formatters, and compiled templates. Generally, you only need to
construct one engine during the lifetime of a program.
let engine = upon::Engine::new();
Next, add_template
is used to compile and store a
template in the engine.
engine.add_template("hello", "Hello {{ user.name }}!")?;
Finally, the template is rendered by fetching it using
get_template
and calling
render
.
let template = engine.get_template("hello").unwrap();
let result = template.render(upon::value!{ user: { name: "John Smith" }}).to_string()?;
assert_eq!(result, "Hello John Smith!");
If the lifetime of the template source is shorter than the engine lifetime
or you don’t need to store the compiled template then you can also use the
compile
function to return the template directly.
let template = engine.compile("Hello {{ user.name }}!")?;
let result = template.render(upon::value!{ user: { name: "John Smith" }}).to_string()?;
assert_eq!(result, "Hello John Smith!");
Further reading
- The
syntax
module documentation outlines the template syntax. - The
filters
module documentation describes filters and how they work. - The
fmt
module documentation contains information on value formatters. - The
examples/
directory in the repository contains concrete code examples.
Features
The following crate features are available.
-
filters
(enabled by default) — Enables support for filters in templates (seeEngine::add_filter
). This does not affect value formatters (seeEngine::add_formatter
). Disabling this will improve compile times. -
serde
(enabled by default) — Enables all serde support and pulls in theserde
crate as a dependency. If disabled then you can userender_from
to render templates and construct the context usingValue
’sFrom
impls. -
unicode
(enabled by default) — Enables unicode support and pulls in theunicode-ident
andunicode-width
crates. If disabled then unicode identifiers will no longer be allowed in templates and.chars().count()
will be used in error formatting.
To disable all features or to use a subset you need to set default-features = false
in your Cargo manifest and then enable the features that you would
like. For example to use serde
but disable filters
and
unicode
you would do the following.
[dependencies]
upon = { version = "...", default-features = false, features = ["serde"] }
Modules
- filters
filters
An abstraction over any filter function or closure. - Types for value formatters.
- Documents the template syntax.
Macros
Structs
- The compilation and rendering engine.
- An error that can occur during template compilation or rendering.
- A renderer that interprets a compiled
Template
. - The template syntax configuration.
- A builder for the syntax configuration.
- A compiled template created using
Engine::compile
. - A reference to a compiled template in an
Engine
. - A member in a value path.
Enums
- A type of function stored in the engine.
- Data to be rendered represented as a recursive enum.
- A key in a value path.
- The type of member access.
Functions
- to_value
serde
Convert aT
to aValue
.
Type Definitions
- A type alias for results in this crate.